/*
 * REFSocketAcceptor.cpp
 *
 *  Created on: May 29, 2013
 *      Author: Lucifer
 */

#include "../pch.h"
#include "REFSocketEventContext.h"
#include "REFIOEventDispatcher.h"
#include "REFIOEventService.h"
#include "REFSocketAcceptor.h"
#include "REFSocketConnection.h"

#ifdef	BFX_IO_HAS_IOCP
#include <Mswsock.h>
#endif

namespace BFX
{

REFSocketAcceptor::REFSocketAcceptor(REFIOEventService* pIOService)
{
	m_bActive = false;
	// TODO: if pIOSService == NULL, set it to the default service.
	m_pIOService = pIOService;
}

REFSocketAcceptor::~REFSocketAcceptor()
{
	StopListening();
}

void REFSocketAcceptor::Listen(REFSocketAddress* pLocalAddress, int nBacklog)
{
	BFX_ENSURE(nBacklog > 0);
	BFX_ASSERT(pLocalAddress != NULL);
	if (m_bActive)
	{
		BFX_DBGWARN_("REFSocketAcceptor")("Already started.");
		return;
	}

	m_pLocalAddress = pLocalAddress;

	REF<REFSocket> pSocket = new REFSocket(pLocalAddress->GetAddressFamily(), SocketType_Stream, SocketProtocolType_Tcp);
	if (!pSocket->Bind(m_pLocalAddress))
		throw SocketException();

	if (!pSocket->Listen(65536))
	{
		StopListening();
		throw SocketException();
	}
#ifdef	BFX_IO_HAS_IOCP
	m_pIOService->BindToCompletionPort(reinterpret_cast<HANDLE>(pSocket->GetFileDescriptor()));
#else
	m_pIOService->GetEventDispatcher()->RegisterFileDescriptor(pSocket->GetFileDescriptor());
#endif

	m_pSocket = pSocket;
	m_bActive = true;
}

void REFSocketAcceptor::StopListening()
{
	if (m_pSocket != NULL)
	{
#ifndef	BFX_IO_HAS_IOCP
		m_pIOService->GetEventDispatcher()->UnregisterFileDescriptor(m_pSocket->GetFileDescriptor());
#endif

		m_pSocket->Close();
		m_pSocket = NULL;
	}
	m_bActive = false;
}

REFSocket* REFSocketAcceptor::GetSocket() const
{
	return m_pSocket;
}

void REFSocketAcceptor::BeginAccept(IOEventCallback callback)
{
	BFX_ENSURE1(m_pSocket != NULL, "");
	BFX_ENSURE1(m_pSocket->IsValid(), "");

#ifdef	BFX_IO_HAS_IOCP

	REF<REFSocket> pAcceptSocket = new REFSocket(m_pSocket->GetAddressFamily(), m_pSocket->GetSocketType(), m_pSocket->GetProtocolType());
	REF<REFSocketAcceptEventContext> pContext = new REFSocketAcceptEventContext(m_pSocket, pAcceptSocket, callback);

	BYTE* pOutputBuffer = pContext->GetBuffer();
	DWORD dwAddressLength = (DWORD) pContext->GetAddressLength();
	DWORD dwBytesTransferred;

	BOOL bRetVal = ::AcceptEx(m_pSocket->GetFileDescriptor(), pAcceptSocket->GetFileDescriptor(), pOutputBuffer, 0, dwAddressLength,
		dwAddressLength, &dwBytesTransferred, (REFSocketAcceptEventContext*)pContext);
	DWORD dwLastError = ::WSAGetLastError();
	if (!bRetVal && dwLastError != WSA_IO_PENDING)
		m_pIOService->OnCompletion(pContext, dwLastError, 0);	// NOTE: should we rises an exception directly?
	else
		m_pIOService->OnPending(pContext);

	pContext->AddRef();	// this object will be release in EndAccept().
#else

	REF<REFSocketAcceptEventContext> pContext = new REFSocketAcceptEventContext(m_pSocket, callback);
	m_pIOService->GetEventDispatcher()->WaitEvent(m_pSocket->GetFileDescriptor(), pContext,
			REFIOEventDispatcher::READ_EVENT);

#endif	//	!BFX_IO_HAS_IOCP
}

REFSocketConnection* REFSocketAcceptor::EndAccept(REFIOEventContext* pContext, REFIOEventService* pIOService)
{
	REF<REFSocketAcceptEventContext> pAcceptContext = static_cast<REFSocketAcceptEventContext*>(pContext);
#ifdef	BFX_IO_HAS_IOCP
	pContext->Release();
#endif

	SocketError error = pAcceptContext->GetSocketError();
	if (error != SocketError_Success)
	{
		throw SocketException(error);
	}
	if (pIOService == NULL)
		pIOService = m_pIOService;

	REFSocket* pAcceptSocket = pAcceptContext->GetAcceptSocket();
	REFSocketConnection* pClient = new REFSocketConnection(pAcceptSocket, pIOService);

	pClient->Autorelease();

	return pClient;
}

} /* namespace BFX */
